The implementation of Object::PrintOn is special insofar that it also handles the
writing of the receiver's observers (see also PrintOnWhenObserved).
Note that fully functional dependent objects and auxiliary objects need not
to be output. It also makes no sense or might even cause problems if non-part
objects are output. The prototypical example for such a non-part object is
the so-called next handler of an EvtHandler.
See also technote 'Object Input/Output'.
I/O of non-regular objects
For all C++ basic data types and standard ET++ built-ins, overloadings
of operator<< and operator>> are provided. For C++ enumerations and for
the type bool declared by ET++ however, this is not the case. Such values
are to be be transformed by means of Enum, and Bool respectively.
Furthermore, there is the pair of the functions PrintString and
ReadString for stream i/o of strings in general (see String.[hC]). It is
strongly recommended to use them for variables of the type char *
or byte *. (The overloaded operator>> for (char *) does not allocate
memory because the operator cannot determine wether this pointer points
to dynamically allocated memory or not.)
Schematic example for PrintOn:
// file: B.h #include "A.h" enum SomeEnum { eVal0, eVal1; }; class B : public A { public: MetaDef(B); B() { ... }; void InitNew(); ostream &PrintOn(ostream &); istream &ReadFrom(istream &); private: Object *anObject0, // part object *anObject1, // part object *someObject; // functional dependent object *auxObject; // independent auxilliar object A someA; Point aPoint; byte *aString; SomeEnum anEnum; bool aBool; }; // file: B.C void B::InitNew() { A::InitNew(); anObject0= 0; // anObject0 is created on demand, anObject1= new AnyObject(); // but anObject1 is created now for some // reasons someObject= 0; // someObject is functional dependent, // but cannot constructed now since // the values of the other variables // is not yet kown. auxObject= new ...; // auxilliar object is created here aString= 0; // assertion } ostream& B::PrintOn(ostream& os) { A::PrintOn(os); // base class' PrintOn !! os << anObject0 SP << anObject1 SP << someA SP << aPoint NL; PrintString(os, aString); // no delimiters needed os << Enum(anEnum) SP << Bool(aBool) NL; return os; } istream& B::ReadFrom(istream& is) { A::ReadFrom(is); // base class' ReadFrom SafeDelete(anObject1); // has been created in B::InitNew os >> anObject0 >> anObject1 >> someA >> aPoint; ReadString(is, &aString); // allocates memory for aString // note the signature of ReadString ! is >> Enum(anEnum) >> Bool(aBool); someObject= ...; // now create functional dependent objects return is; }Hint: Since a file written with Object I/O is text, it can be inspected with a text editor. Appropriately placing NL's eases such inspections. Method is often overridden.
object i/o, overrider interface
Note that the pointer book-keeping is done for Objects only, but not by
ReadString or PrintString.
Non-regular pointers, for instance (int *) or (Point *), are written as
hexadecimal values. Output of such pointers makes no sense in general. The
client should either avoid such cases, or handle them by additional
measures. The better solution is to avoid them.
Note that the pointer book-keeping is global; the book-keeping
table must be reset in some cases by the client (see ClassManager::Reset).
If output of an object graph is unintentionally interlaced with Object
input, for instance by a DeepClone operation, then the output written to
the stream is wrong since DeepClone called ClassManager::Reset. There are
other comparable cases. This problem will be alleviated in future
versions by a slightly different concept that assigns the book-keeping task
to the so-called object streams.